import datetime
import re  # 导入正则表达式模块，用于手机号格式验证

from flask import request, jsonify, current_app,session  # 从Flask框架中导入相关模块
from app import redis, db  # 导入redis数据库实例，SQLAlchemy数据库实例，和Flask管理模块
from app.api_v1_0 import api  # 导入API蓝图
from app.models import User,UserApply  # 导入用户模型

from app.utils.response_code import RET  # 导入自定义的响应码

from sqlalchemy.exc import IntegrityError  # 导入SQLAlchemy中的异常类，用于捕获数据库完整性错误
from app import constants

@api.route('test', methods=["POST", "GET"])
def test():
    print("enter,", request.get_json())
    return "yes"

@api.route('test2', methods=['GET'])
def test2():
    session['user_id'] = 'user.id'  # 将用户ID存入session
    session['mobile'] = 'mobile'  # 将手机号存入session
    session['name'] = 'name'  # 将用户名存入session
    session['power'] = 'user'  # 新用户默认权限为普通
    print("*********", session.get('name'), session.get('user_id'))
    # 返回结果
    return 'ok'  # 返回注册成功信息

@api.route('userApply', methods=["POST"])
def register():

    request_form = request.get_json().get('form')

    name = request_form.get('name')
    mobile = request_form.get('mobile')
    password = request_form.get('password')
    password2 = request_form.get('password2')
    mail = request_form.get('mail')
    print(f"前端为：name:{name}, mobile:{mobile}, password:{password}, password2:{password2}, mail:{mail}")

    if not all([name, mobile, password, password2]):
        return jsonify(errno=RET.PARAMERR, errmsg="请求参数缺失")

    # 判断手机号码格式
    if not re.match(r'^1[3-9]\d{9}$', mobile):  # 使用正则表达式匹配手机号格式
        # 表示手机号码格式不对
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")  # 返回手机号格式错误信息

    if password != password2:
        return jsonify(errno=RET.PARAMERR, errmsg="密码和确认密码不一致")

    # 保存用户的申请数据
    user = UserApply(name=name, mobile=mobile)
    user.password = password
    if(mail):
        user.mail = mail
    try:
        db.session.add(user)  # 添加用户到数据库会话
        db.session.commit()  # 提交数据库会话
    except IntegrityError as e:  # 捕获数据库完整性错误
        db.session.rollback()  # 回滚数据库会话
        current_app.logger.error(e)  # 记录错误日志
        return jsonify(errno=RET.DATAEXIST, errmsg="该手机号码已经存在")  # 返回手机号已注册错误
    except Exception as e:
        db.session.rollback()  # 回滚数据库会话
        return jsonify(errno=RET.DBERR, errmsg="查询数据库异常")  # 返回数据库查询错误

    # 返回结果
    return jsonify(errno=RET.OK, errmsg="申请成功，请等候管理员同意")  # 返回注册成功信息

@api.route('/sessions', methods=['POST'])
def login():
    """
    用户登录
    参数：手机号码、密码
    方式：json
    :return:
    """
    # 获取参数
    request_json = request.get_json().get('form')
    # 手机号码
    mobile = request_json['mobile']
    # 密码
    password = request_json['password']

    # 校验参数完整性
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")

    # 校验手机号码格式
    if not re.match(r'^1[3-9]\d{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号码格式错误")

    # 判断错误次数是否超过限制，如果超过限制，则返回
    # 通过redis记录次数，保存格式："access_nums_请求的ip": "次数”
    user_ip = request.remote_addr  # 获取用户ip地址

    try:
        # 从Redis数据库中获取名为access_nums_{user_ip}的键对应的值，即用户输入错误的次数。
        access_nums = redis.get(f'access_nums_{user_ip}')
    except Exception as e:
        current_app.logger.error(e)
    else:
        # 检查从Redis中获取的错误次数是否不为None且大于等于设定的最大错误次数。
        if access_nums is not None and int(access_nums.decode("utf-8")) >= constants.LOGIN_ERROR_MAX_TIMES:
            # 果错误次数达到或超过最大错误次数，则返回一个JSON响应
            return jsonify(errno=RET.REQERR, errmsg="登录错误次数过多，请稍后重试")

    # 在数据库中根据手机号码查询用户的数据对象
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取用户信息失败")

    # 用数据库的密码与用户输入的密码进行对比验证
    # 如果查询用户为None,或者验证密码为false，则表示
    if user is None or user.check_password(password) is False:
        # 如果验证失败，记录错误次数，返回信息
        try:
            """
            INCR命令用于对存储在Redis中的字符串类型的数字数据进行加一操作。如果数据一开始不存在，则会被初始化为1。
            这个命令非常适合用于计数器的场景，可以方便地对计数器进行自增操作，并且在数据不存在时会自动初始化为1，简化了代码逻辑。
            希望这个信息对你有帮助，如果有其他问题或需要进一步解释，请随时告诉我。
            """
            redis.incr(f'access_nums_{user_ip}')
            # 设置过期时间
            redis.expire(f'access_nums_{user_ip}', constants.LOGIN_ERROR_FORBID_TIME)
        except Exception as e:
            current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取用户信息失败")

    try:
        user.login_time = datetime.datetime.now()
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)


    # 如果验证密码相同成功，保存登录状态，在session中保存用户信息
    session['name'] = user.name
    session['mobile'] = user.mobile
    session['user_id'] = user.id
    session['power'] = user.power

    return jsonify(errno=RET.OK, errmsg="登录成功")


@api.route('/session', methods=['GET'])
def get_sessions():
    """
    检查登录状态
    :return:
    """
    # 尝试从session中获取用户的名字
    name = session.get('name')

    print("/session:", name)

    # 如果session中数据name名字存在，则表示用户已登录，否则未登录
    if name is not None:
        # 如果不为None,则表示用户已经登录，这时候把session中查询出来的用户名返回给前端显示
        return jsonify(errno=RET.OK, errmsg="true", data={"name": name}) # 返回权限和昵称
    else:
        # 为None则表示用户未登录
        return jsonify(errno=RET.SESSIONERR, errmsg="false")


@api.route('/session', methods=['DELETE'])
def logout():
    """
    退出登录（删除session中的数据即可）
    :return:
    """
    session.clear()
    return jsonify(errno=RET.OK, errmsg="ok")